package com.iflytek.jzcpx.procuracy.ocr.service.impl;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.iflytek.jzcpx.procuracy.common.util.JSONUtil;
import com.iflytek.jzcpx.procuracy.ocr.common.enums.OcrTaskStatusEnum;
import com.iflytek.jzcpx.procuracy.ocr.dao.OcrTaskDao;
import com.iflytek.jzcpx.procuracy.ocr.entity.OcrTask;
import com.iflytek.jzcpx.procuracy.ocr.service.OcrFileService;
import com.iflytek.jzcpx.procuracy.ocr.service.OcrTaskService;
import com.iflytek.jzcpx.procuracy.tools.common.PropUtils;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.collections4.MapUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.time.DateUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.Assert;

/**
 * @author <a href=mailto:ktyi@iflytek.com>伊开堂</a>
 * @date 2019-08-12 19:28
 */
@Service
public class OcrTaskServiceImpl extends ServiceImpl<OcrTaskDao, OcrTask> implements OcrTaskService {
    private static final Logger logger = LoggerFactory.getLogger(OcrTaskServiceImpl.class);

    @Autowired
    private OcrFileService ocrFileService;

    @Override
    public Long create(String systemid, String dwbm, String bsbh, String taskid,String bmsah) {
        OcrTask task = new OcrTask();
        task.setSystemid(systemid);
        task.setDwbm(dwbm);
        task.setBsbh(bsbh);
        task.setTaskid(taskid);
        task.setStatus(OcrTaskStatusEnum.INIT.toString());
        task.setBmsah(bmsah);
        Date now = new Date();
        task.setCreateTime(now);
        task.setUpdateTime(now);

        boolean saveSuccess = save(task);

        logger.info("保存ocr批量任务完成, taskId: {}", task.getId());
        return saveSuccess ? task.getId() : null;
    }

    @Override
    public boolean abnormalEnd(Long taskId, String errorInfo) {
        OcrTask params = new OcrTask();
        params.setId(taskId);
        params.setRemark(errorInfo);
        params.setUpdateTime(new Date());
        params.setStatus(OcrTaskStatusEnum.END.name());
        logger.info("ocr 批量识别任务非正常结束, taskId[ {} ], errorInfo[ {} ]", taskId, errorInfo);
        return updateById(params);
    }

    @Override
    public boolean updateStatus(Long taskId, OcrTaskStatusEnum targetStatusEnum) {
        OcrTask params = new OcrTask();
        params.setId(taskId);
        params.setStatus(targetStatusEnum.toString());
        params.setUpdateTime(new Date());
        logger.info("ocr 批量识别任务状态更新, taskId[ {} ], targetStatusEnum[ {} ]", taskId, targetStatusEnum);
        return updateById(params);
    }

    @Override
    public boolean updateStatusAndPushInfo(Long taskId, OcrTaskStatusEnum targetStatusEnum, String resultPushInfo) {
        OcrTask params = new OcrTask();
        params.setId(taskId);
        params.setStatus(targetStatusEnum.toString());
        params.setUpdateTime(new Date());
        params.setResultPushInfo(resultPushInfo);
        logger.info("ocr 批量识别任务推送状态更新, taskId[ {} ], targetStatusEnum[ {} ], resultPushInfo: {}",
                taskId, targetStatusEnum, resultPushInfo);

        return updateById(params);
    }

    @Override
    public List<Long> findPushableTaskId() {
        Integer gapDays = PropUtils.getInt("pushable_recog_task_days", 365);
        Date startTime = DateUtils.addDays(new Date(), -1 * gapDays);

        // 状态为处理中, 所有子任务全部完成
        LambdaQueryWrapper<OcrTask> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.select(OcrTask::getId).eq(OcrTask::getStatus, OcrTaskStatusEnum.PROCESSING.name()).ge(
                OcrTask::getCreateTime, startTime);

        List<OcrTask> taskList = list(queryWrapper);
        if (CollectionUtils.isEmpty(taskList)) {
            return null;
        }

        // 查询出有子任务未完成的 taskId
        List<Long> taskIdList = taskList.stream().map(OcrTask::getId).collect(Collectors.toList());
        Map<Long, List<Long>> undoneFileIdMap = ocrFileService.findUndoneFileIdMap(taskIdList);
        if (MapUtils.isNotEmpty(undoneFileIdMap)) {
            // 移除未完成的, 剩下的任务就是已完成的, 可以推送结果了
            taskIdList.removeAll(undoneFileIdMap.keySet());
        }

        if (CollectionUtils.isEmpty(taskIdList)) {
            return null;
        }

        logger.info("更新OCR识别任务状态为[提交中], idList: {}", JSONUtil.toStrDefault(taskIdList));
        LambdaUpdateWrapper<OcrTask> wrapper = new LambdaUpdateWrapper<>();
        // 达梦数据库只支持2048个入参
        int size = taskIdList.size();
        int step = 2000;
        int start, end = 0;
        for (int i = 0; i < Math.ceil(size / (float) step); i++) {
            start = i * step;
            end = Math.min(size, start + step);
            List<Long> subList = taskIdList.subList(start, end);

            wrapper.in(OcrTask::getId, subList).set(OcrTask::getStatus, OcrTaskStatusEnum.SUBMITTING.name());
            update(wrapper);
        }

        return taskIdList;
    }

    @Override
    public OcrTask findByBmsah(String bmsah) {
        if (StringUtils.isEmpty(bmsah)) {
            return null;
        }

        QueryWrapper<OcrTask> queryWrapper = new QueryWrapper<>();
        queryWrapper.lambda().like(OcrTask::getBmsah, bmsah);
        List<OcrTask> ocrTasks = list(queryWrapper);
        return CollectionUtils.isEmpty(ocrTasks) ? null : ocrTasks.get(0);
    }

    /**
     * 更新失败次数
     *
     * @param taskId 任务 id
     * @return 更新成功与否
     */
    @Override
    public boolean updateTimes(Long taskId) {
	if (null == taskId) {
		return false;
	}
	OcrTask ocrTask = getById(taskId);
	if (null == ocrTask) {
		logger.error("根据taskId查询记录不存在,ocrTask:{}",ocrTask);
		return false;
	}
	OcrTask params = new OcrTask();
	params.setId(taskId);
	params.setUpdateTime(new Date());
	params.setFailNums(ocrTask.getFailNums() + 1);
	logger.info("ocr 推送失败次数更新, taskId[ {} ], params: {}", taskId, params.getFailNums());
	return updateById(params);
    }

    @Override
    public int cleanData(Date startDatetime, Date endDatetime) {
        logger.info("清理ocrTask数据, 起止时间: {} - {}", startDatetime, endDatetime);
        Assert.isTrue(startDatetime != null || endDatetime != null, "开始和结束时间不能同时为空");

        int cleanCount = 0;

        List<OcrTask> ocrTasks = null;
        int pageSize = 100;
        do {
            Page<OcrTask> page = new Page<>(1, pageSize);
            LambdaQueryWrapper<OcrTask> wrapper = new LambdaQueryWrapper<>();
            if (startDatetime != null) {
                wrapper.gt(OcrTask::getCreateTime, startDatetime);
            }
            if (endDatetime != null) {
                wrapper.lt(OcrTask::getCreateTime, endDatetime);
            }
            IPage<OcrTask> ocrTaskPage = this.baseMapper.selectPage(page, wrapper);
            if (ocrTaskPage == null || CollectionUtils.isEmpty(ocrTaskPage.getRecords())) {
                logger.info("没有待清理的ocrTask数据了");
                return cleanCount;
            }

            ocrTasks = ocrTaskPage.getRecords();
            logger.debug("查询到待清理的ocrTask数据, size: {}, page: {}", ocrTasks.size(), page);

            for (OcrTask ocrTask : ocrTasks) {
                logger.info("删除ocrTask数据, ocrTask: {}", ocrTask);
                if (removeById(ocrTask)) {
                    cleanCount++;
                }
            }
        } while (CollectionUtils.size(ocrTasks) >= pageSize);

        return cleanCount;
    }

    @Override
    public List<OcrTask> listByBsbh(String bsbh) {
        if (StringUtils.isBlank(bsbh)) {
            return new ArrayList<>();
        }
        QueryWrapper<OcrTask> queryWrapper = new QueryWrapper<>();
        queryWrapper.lambda().eq(OcrTask::getBsbh, bsbh);
        return list(queryWrapper);
    }
}
